package at.fhv.androideightpuzzle.domain;

import java.util.Random;

/**
 * <code>PuzzleBoard</code> is the board with the puzzle parts on it.
 * @author psp5448
 *
 */
public class PuzzleBoard {
	
	/**
	 * The dimension of the puzzle board.
	 */
	public static final int DIMENSION=3;
	
	private PuzzlePart[][] m_parts;
	private int[] m_endState;
	
	/**
	 * Creates a new <code>PuzzleBoard</code>
	 * @param parts the puzzle parts
	 * @param endState the end state
	 */
	public PuzzleBoard(PuzzlePart[][] parts,int[] endState){
		m_parts=parts;
		m_endState=endState;
	}
	
	public PuzzleBoard(PuzzlePart[] parts,int[] endState){
		m_endState=endState;
		
		m_parts=new PuzzlePart[PuzzleBoard.DIMENSION][PuzzleBoard.DIMENSION];
		
		for(int i=0; i<parts.length; i++){
			int row=i/PuzzleBoard.DIMENSION;
			int col=i%PuzzleBoard.DIMENSION;
			
			m_parts[row][col]=parts[i];
		}
	}
	
	public PuzzleBoard(int[] parts,int[] endState,int emptyPartNumber){
		m_parts=new PuzzlePart[PuzzleBoard.DIMENSION][PuzzleBoard.DIMENSION];
		
		for(int i=0; i<parts.length; i++){
			int row=i/PuzzleBoard.DIMENSION;
			int col=i%PuzzleBoard.DIMENSION;
			
			if(parts[i]!=emptyPartNumber){
				m_parts[row][col]=new PuzzlePart(parts[i]);
			}else{
				m_parts[row][col]=new EmptyPart(emptyPartNumber);
			}
		}
		
		m_endState=endState;
	}
	
	/**
	 * Returns true, if every part is at it's end position
	 * @return true, if the puzzle is solved
	 */
	public boolean isFinished(){
		for(int i=0; i<m_endState.length; i++){
			int row=i/PuzzleBoard.DIMENSION;
			int col=i%PuzzleBoard.DIMENSION;
			
			if(m_endState[i]!=m_parts[row][col].getNumber()){
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Swaps the empty part.
	 * @param direction the direction for the swap
	 */
	public int[][] swapEmptyPart(SwapDirection direction){
		Position emptyPartPosition=getEmptyPartPosition();
		PuzzlePart emptyPart=m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()];
		
		switch(direction){
			case DOWN:
				if(emptyPartPosition.getRow()<2){
					PuzzlePart part=m_parts[emptyPartPosition.getRow()+1][emptyPartPosition.getColumn()];
					
					m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()]=part;
					m_parts[emptyPartPosition.getRow()+1][emptyPartPosition.getColumn()]=emptyPart;
				}
				
				break;
				
			case LEFT:
				if(emptyPartPosition.getColumn()>0){
					PuzzlePart part=m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()-1];
					
					m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()]=part;
					m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()-1]=emptyPart;
				}
				
				break;
				
			case RIGHT:
				if(emptyPartPosition.getColumn()<2){
					PuzzlePart part=m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()+1];
					
					m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()]=part;
					m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()+1]=emptyPart;
				}
				
				break;
				
			case UP:
				if(emptyPartPosition.getRow()>0){
					PuzzlePart part=m_parts[emptyPartPosition.getRow()-1][emptyPartPosition.getColumn()];
					
					m_parts[emptyPartPosition.getRow()][emptyPartPosition.getColumn()]=part;
					m_parts[emptyPartPosition.getRow()-1][emptyPartPosition.getColumn()]=emptyPart;
				}
				
				break;
				
			default:
				return getState();
		}
		
		return getState();
	}
	
	public int[][] swapPart(Position position){
		Position emptyPartPosition=getEmptyPartPosition();
		
		if((position.getRow()==(emptyPartPosition.getRow()-1)) && (position.getColumn()==emptyPartPosition.getColumn())){
			return swapEmptyPart(SwapDirection.UP);
		}else if(position.getRow()==(emptyPartPosition.getRow()+1) && (position.getColumn()==emptyPartPosition.getColumn())){
			return swapEmptyPart(SwapDirection.DOWN);
		}else if((position.getColumn()==(emptyPartPosition.getColumn()-1)) && (position.getRow()==emptyPartPosition.getRow())){
			return swapEmptyPart(SwapDirection.LEFT);
		}else if((position.getColumn()==(emptyPartPosition.getColumn()+1)) && (position.getRow()==emptyPartPosition.getRow())){
			return swapEmptyPart(SwapDirection.RIGHT);
		}
		
		return getState();
	}
	
	/**
	 * Returns the position of the empty part.
	 * @return the position of the empty part
	 */
	public Position getEmptyPartPosition(){
		int row=0;
		int col=0;
		
		while(row<PuzzleBoard.DIMENSION){
			if(m_parts[row][col] instanceof EmptyPart){
				return new Position(row,col);
			}
			
			//matrix shift
			col++;
			
			if(col>=PuzzleBoard.DIMENSION){
				col=0;
				row++;
			}
		}
		
		return null;
	}
	
	/**
	 * Shuffles the puzzle parts.
	 */
	public void shuffle(){
		Random random=new Random();
		
		for(int i=0; i<44; i++){
			int next1=random.nextInt(9);
			int next2=random.nextInt(9);
			
			int row1=next1/PuzzleBoard.DIMENSION;
			int col1=next1%PuzzleBoard.DIMENSION;
			
			int row2=next2/PuzzleBoard.DIMENSION;
			int col2=next2%PuzzleBoard.DIMENSION;
			
			PuzzlePart tempPart=m_parts[row1][col1];
			m_parts[row1][col1]=m_parts[row2][col2];
			m_parts[row2][col2]=tempPart;
		}
	}
	
	/**
	 * Returns the parts.
	 * @return the parts
	 */
	public PuzzlePart[][] getParts(){
		return m_parts;
	}
	
	/**
	 * Returns the end state.
	 * @return the end state
	 */
	public int[] getEndState(){
		return m_endState;
	}
	
	public EmptyPart getEmptyPart(){
		Position pos=getEmptyPartPosition();
		
		if(pos!=null){
			return ((EmptyPart)m_parts[pos.getRow()][pos.getColumn()]);
		}else{
			return null;
		}
	}
	
	public int[][] getState(){
		int[][] state=new int[PuzzleBoard.DIMENSION][PuzzleBoard.DIMENSION];
		
		int row=0;
		int col=0;
		
		while(row<PuzzleBoard.DIMENSION){
			state[row][col]=m_parts[row][col].getNumber();
			
			//matrix shift
			col++;
			
			if(col>=PuzzleBoard.DIMENSION){
				col=0;
				row++;
			}
		}
		
		return state;
	}
}
